import { MaybeRef } from '@vueuse/core'
import { Ref, unref, onMounted } from 'vue'
import { findDOMNode } from '../props-util'

function getPosition(e: MouseEvent | TouchEvent) {
  if ('touches' in e) {
    return [e.touches[0].pageX, e.touches[0].pageY]
  } else {
    return [e.pageX, e.pageY]
  }
}

type DraggableOptions = Partial<{
  dragTarget: MaybeRef<HTMLElement>,
  onDragStart: (e: MouseEvent | TouchEvent) => void
  onDragging: (e: MouseEvent | TouchEvent) => void
  onDragEnd: (e?: MouseEvent | TouchEvent) => void
  x: Ref<number>,
  y: Ref<number>
}>

export default function useDraggable(el: MaybeRef<HTMLElement>, options?: DraggableOptions) {
  let startX = 0, startY = 0,
      startPageX = 0, startPageY = 0
  
  function onMouseDown(e: MouseEvent) {
    e.preventDefault()
    const style = getComputedStyle(unref(el))
    startX = style.left ? parseInt(style.left) : 0
    startY = style.top ? parseInt(style.top) : 0
    ;[startPageX, startPageY] = getPosition(e)
    window.addEventListener('mousemove', onMouseMove)
    window.addEventListener('mouseup', onMouseUp)
    options.onDragStart?.(e)
  }

  function onMouseMove(e: MouseEvent) {
    e.preventDefault()
    const _el = unref(el)
    const [pageX, pageY] = getPosition(e)
    const x = startX + pageX - startPageX, y = startY + pageY - startPageY
    setPosition(x, y)
    _el.style.zIndex = '1'
    options.onDragging?.(e)
  }

  function onMouseUp(e?: MouseEvent) {
    window.removeEventListener('mousemove', onMouseMove)
    window.removeEventListener('mouseup', onMouseUp)
    options.onDragEnd?.(e)
  }


  function setPosition(x: number, y: number) {
    const _el = unref(el)
    _el.style.left = x + 'px'
    _el.style.top = y + 'px'
    if (options.x) options.x.value = x
    if (options.y) options.y.value = y
  }

  let oldTarget: HTMLElement
  function enable(bool: boolean) {
    if (bool) {
      enable(false)
      oldTarget = findDOMNode(unref(options.dragTarget) || unref(el))
      oldTarget?.addEventListener('mousedown', onMouseDown)
    } else {
      oldTarget?.removeEventListener('mousedown', onMouseDown)
      oldTarget = null
      onMouseUp()
    }
  }

  function reset() {
    const _el = unref(el)
    _el.style.left = ''
    _el.style.top = ''
    _el.style.zIndex = ''
  }
  
  onMounted(() => {
    enable(true)
  })

  return {
    enable,
    reset
  }
}